package xin.yangshuai.springsecurity01.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.*;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
//@EnableWebSecurity
@EnableMethodSecurity //开启基于方法的授权
public class WebSecurityConfig {

//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        // 此时配置文件中的用户名和密码将不可用
//        manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
//        return manager;
//    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer = new Customizer<ExpressionUrlAuthorizationConfigurer<org.springframework.security.config.annotation.web.builders.HttpSecurity>.ExpressionInterceptUrlRegistry>() {
            @Override
            public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry) {

                // 具有USER_LIST权限的用户可以访问/user/list
                // expressionInterceptUrlRegistry.requestMatchers("/user/list").hasAuthority("USER_LIST");
                // 具有USER_ADD权限的用户可以访问/user/add
                // expressionInterceptUrlRegistry.requestMatchers("/user/add").hasAuthority("USER_ADD");

                // 具有ADMIN角色的用户可以访问/user/**
                // expressionInterceptUrlRegistry.requestMatchers("/user/**").hasRole("ADMIN");

                // RBAC，Role-Based Access Control，基于角色的访问控制

                // 基于方法的授权，过滤器中就不需要授权的配置了

                expressionInterceptUrlRegistry
                        // 对所有请求开启授权保护
                        .anyRequest()
                        // 已经认证的请求会被自动授权
                        .authenticated();
            }
        };

        // 开启授权保护
        http.authorizeRequests(authorizeRequestsCustomizer);

        // 使用表单授权方式（登录页、登录流程、登出页、登出流程）
        // http.formLogin(Customizer.withDefaults());

        // 使用基本授权方式（浏览器自带的登录页面，无默认登出页）
        // http.httpBasic(Customizer.withDefaults());

        // 自定义登录页面
        http.formLogin(new Customizer<FormLoginConfigurer<HttpSecurity>>() {
            @Override
            public void customize(FormLoginConfigurer<HttpSecurity> httpSecurityFormLoginConfigurer) {
                // 自定义登录页，并且设置无需授权允许访问
                httpSecurityFormLoginConfigurer.loginPage("/login").permitAll();
                // 配置自定义表单的用户名参数，默认值：username
                httpSecurityFormLoginConfigurer.usernameParameter("myusername");
                // 配置自定义表单的密码参数，默认值：password
                httpSecurityFormLoginConfigurer.passwordParameter("mypassword");
                // 校验失败时跳转的地址，默认值：/login?error
                httpSecurityFormLoginConfigurer.failureUrl("/login?error");
                // 认证成功时的处理
                httpSecurityFormLoginConfigurer.successHandler(new MyAuthenticationSuccessHandler());
                // 认证失败时的处理
                httpSecurityFormLoginConfigurer.failureHandler(new MyAuthenticationFailureHandler());
            }
        });

        http.logout(new Customizer<LogoutConfigurer<HttpSecurity>>() {
            @Override
            public void customize(LogoutConfigurer<HttpSecurity> httpSecurityLogoutConfigurer) {
                // 注销成功时的处理
                httpSecurityLogoutConfigurer.logoutSuccessHandler(new MyLogoutSuccessHandler());
            }
        });

        http.exceptionHandling(new Customizer<ExceptionHandlingConfigurer<HttpSecurity>>() {
            @Override
            public void customize(ExceptionHandlingConfigurer<HttpSecurity> httpSecurityExceptionHandlingConfigurer) {
                // 请求未认证的处理
                httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(new MyAuthenticationEntryPoint());
                // 请求被拒绝的处理（没有权限）
                httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(new MyAccessDeniedHandler());
            }
        });

        http.sessionManagement(new Customizer<SessionManagementConfigurer<HttpSecurity>>() {
            @Override
            public void customize(SessionManagementConfigurer<HttpSecurity> httpSecuritySessionManagementConfigurer) {
                // 同一账号，最多可以同时在几个客户登录。1表示只允许有一个客户端登录
                // 默认策略，后登录的会把先登录的挤掉，先登录的会被设置为会话过期
                httpSecuritySessionManagementConfigurer.maximumSessions(1).expiredSessionStrategy(new MySessionInformationExpiredStrategy());
            }
        });

        // 跨域
        http.cors(Customizer.withDefaults());

        // 关闭csrf攻击防御
        http.csrf(new Customizer<CsrfConfigurer<HttpSecurity>>() {
            @Override
            public void customize(CsrfConfigurer<HttpSecurity> httpSecurityCsrfConfigurer) {
                httpSecurityCsrfConfigurer.disable();
            }
        });

        return http.build();
    }
}
